package com.stottlerhenke.simbionic.editor.parser;

import java.util.ArrayList;
import java.util.List;

import com.stottlerhenke.simbionic.api.SB_Exception;
import com.stottlerhenke.simbionic.api.SB_VarType;
import com.stottlerhenke.simbionic.api.SB_Vector;
import com.stottlerhenke.simbionic.common.classes.SB_ClassMethod;
import com.stottlerhenke.simbionic.common.parser.EBinaryOp;
import com.stottlerhenke.simbionic.common.parser.EParamDir;
import com.stottlerhenke.simbionic.common.parser.EParseResult;
import com.stottlerhenke.simbionic.common.parser.ETernaryOp;
import com.stottlerhenke.simbionic.common.parser.EUnaryOp;
import com.stottlerhenke.simbionic.common.parser.SB_ExpressionNode;
import com.stottlerhenke.simbionic.common.parser.SB_ObjectRefWrapper;
import com.stottlerhenke.simbionic.common.parser.SB_Parser;
import com.stottlerhenke.simbionic.common.parser.SB_ParserAppInterface;
import com.stottlerhenke.simbionic.editor.SB_EditorDataInterface;
import com.stottlerhenke.simbionic.editor.SB_TypeManager;
import com.stottlerhenke.simbionic.editor.gui.SB_Polymorphism;

public class SB_ParserEditorInterface extends SB_ParserAppInterface {
	private SB_EditorDataInterface _dataInterface;
	private SB_TypeManager _typeManager;
	private static SB_Parser _parser;
	
	public static void setParser(SB_Parser parser)
	{
	    _parser = parser;
	}
	
	public static SB_Parser getParser()
	{
	    return _parser;
	}

	public SB_ParserEditorInterface(SB_EditorDataInterface iface) {
		_dataInterface = iface;
	}
	
	public void setTypeManager(SB_TypeManager typeManager){
		_typeManager = typeManager;
	}
	
	// allow the parser to parse variables
	public void setPoly(SB_Polymorphism poly) { 
		SB_EditorDataInterface.setPoly(poly); 
	}
	
	public SB_ExpressionNode CreateErrorNode(String msg, String badName /* ="" */) {
		
	    String error = new String(this.GetCompleteExpression() + ": " + msg);

	    SB_ErrorNode err = new SB_ErrorNode(error, badName);

		return err;
	}

	public SB_ExpressionNode CreateErrorNode(String msg) {
		return new SB_ErrorNode(msg, "");
	}

	public SB_ExpressionNode CreateConstantNode(float value) {
		return new SB_FloatConstantNode(value);
	}

	public SB_ExpressionNode CreateConstantNode(int value) {
		return new SB_IntegerConstantNode(value);
	}

	public SB_ExpressionNode CreateConstantNode(boolean value) {
		return new SB_BooleanConstantNode(value);
	}

	public SB_ExpressionNode CreateConstantNode(String value) {
		return new SB_StringConstantNode(value);
	}

	public SB_ExpressionNode CreateConstantNode(String value, boolean symbol) {
		return new SB_StringConstantNode(value, symbol);
	}
	
	public SB_ExpressionNode CreateConstantNode(double x, double y, double z) {
		return new SB_VectorConstantNode(x, y, z);
	}

	public SB_ExpressionNode CreateArrayNode(ArrayList array) {
		return new SB_ArrayNode(array);
	}

	public SB_ExpressionNode CreateClassNode(String className) {
	    if( _dataInterface.LookupClassName(className) != null )
	        	return new SB_ClassNode(className);
	    else
	        return null;
	}

	public SB_ExpressionNode CreateVariableNode(String varName) {
		// check if this variable has been defined
		SB_VarType result = _dataInterface.LookupVariable(varName);
		if (result == SB_VarType.kNoSuchName) {
			return CreateErrorNode(new String("Unknown variable " + varName
					+ "."), varName);
		}

		return new SB_VariableNode(varName, result);
	}

	public SB_ExpressionNode CreateFunctionNode(String funcName) {
		// check if this function has been defined
		SB_VarType result = _dataInterface.LookupFunction(funcName);
		if (result == SB_VarType.kNoSuchName) {
			return CreateErrorNode(new String("Unknown function " + funcName
					+ "."), funcName);
		}

		return new SB_FunctionNode(funcName, result);
	}

	/**
	 * @return the fully qualified class of the given expression
	 */
	String getClassName(SB_ExpressionNode instance)
	{
	    if( instance instanceof SB_BinaryOpNode )
	    {
	        SB_MethodNode method = (SB_MethodNode) ((SB_BinaryOpNode)instance).GetRightArg();
	    
	    		if( method != null )
	    		{
	    		    SB_VarType userType = method.GetType();
	    		    String userClass = _dataInterface.LookupClassName(userType);
	    		    return userClass;
	    		}
	    }
	    else
	    if( instance instanceof SB_ClassNode)
	    {
	        String alias = ((SB_ClassNode)instance).GetClass();
	        return _dataInterface.LookupClassName(alias);
	    }
	    else
	    {
	        String varName = ((SB_ParseNode)instance).ToString();
	    		String className = _dataInterface.LookupVariableClassName(varName);
	    		return className; 
	    }
	    
	    return "";
	}
	
	public SB_ExpressionNode CreateMethodNode(SB_ExpressionNode instance, String methodName) 
	{
	    String className = getClassName(instance);

	    if (className != null)
	    {
	        boolean bExists = _dataInterface.MethodExists(className, methodName);
	        
	        if( ! bExists )
	        {
	      			return CreateErrorNode( new String("Unknown method '" + className + "." + methodName));
	        }
	    }
	    else
	    {
	        // either there's an error on the lhs of the dot or the class doesn't exist
	        if( instance instanceof SB_ErrorNode )
	        {
	  	        return CreateErrorNode( new String("Cannot invoke method '" + methodName + "' on non-class variable '" + ((SB_ErrorNode)instance).GetInvalidName() + "'."));
	        }
	        else
	        if( instance instanceof SB_VariableNode)
	        {
		    			return CreateErrorNode(new String("Variable '" + ((SB_VariableNode)instance).ToString() +  "' has an unknown class type."));
	        }	        
	        return CreateErrorNode(new String("Unknown class error in invocation of method '" + methodName + "'." )); 
	    }
	    
	    // don't know the return type until we match the actual params to the right overload
	    return new SB_MethodNode(className, methodName);
	}

	public void AddArgument(SB_ExpressionNode func, SB_ExpressionNode arg) 
	{
	  SB_FunctionNode funcNode = (SB_FunctionNode) (func);
		funcNode.AddArgument((SB_ParseNode) (arg));
	}

	public EParseResult FinishFunction(SB_ObjectRefWrapper funcWrapper)
	{
	    EParseResult result = EParseResult.kMatch;
	    SB_ExpressionNode func = (SB_ExpressionNode) funcWrapper.GetObjectRef();
	    SB_ExpressionNode error = null;
	    
	    if( func instanceof SB_MethodNode)
	    {
	        SB_MethodNode methodNode = (SB_MethodNode) func; 
	        
	        if (_dataInterface.LookupClassDescription(methodNode.GetClass()) == null)
	            error = CreateErrorNode("No class description found for class: " + methodNode.GetClass());
	        else
	        {
	            //Convert parseNodes to class objects
	            ArrayList classArgs = new ArrayList(); //contains Class objects
	            ArrayList args = methodNode.GetArgs();
	            for(int x = 0; x < args.size(); x++)
	            {
	                SB_ParseNode node = (SB_ParseNode) args.get(x);
	                
	                if(!node.IsValid())
	                {
	                    error = node;
	                    break;
	                }
	                
	                //Check to see if the parameter is a defined type
	                SB_VarType type = node.GetType();
	                if( type == null ||  type == SB_VarType.kNoSuchName )
	                {
	                    error = CreateErrorNode("Parameter is undefined: " + node.ToString());
	                    break;
	                }
	                else if(type.equals(SB_VarType.kVoid))
	                {
	                	error = CreateErrorNode("Parameter type cannot be void");
	                	break;
	                }
	                else if(type.equals(SB_VarType.kFloat))
	                	classArgs.add(Float.TYPE);
	                else if(type.equals(SB_VarType.kInteger))
	                	classArgs.add(Integer.TYPE);
	                else if(type.equals(SB_VarType.kBoolean))
	                	classArgs.add(Boolean.TYPE);
	                else if(type.equals(SB_VarType.kString))
	                	classArgs.add(String.class);
	                else if(type.equals(SB_VarType.kArray) || type.equals(SB_VarType.kTable))
	                	classArgs.add(List.class);
	                else if(type.equals(SB_VarType.kEntity))
	                	classArgs.add(Long.TYPE);
	                else if(type.equals(SB_VarType.kVector))
	                	classArgs.add(SB_Vector.class);
	                else if(type.equals(SB_VarType.kShort))
	                	classArgs.add(Short.TYPE);
	                else if(type.equals(SB_VarType.kByte))
	                	classArgs.add(Byte.TYPE);
	                else if(type.equals(SB_VarType.kChar))
	                	classArgs.add(Character.TYPE);
	                else if(type.equals(SB_VarType.kDouble))
	                	classArgs.add(Double.TYPE);
	                else
	                {
		                String argClassName = _dataInterface.LookupClassName(type);
		                try
		                {
		                    classArgs.add( Class.forName(argClassName) );
		                }
		                catch(ClassNotFoundException ex)
		                {
		                    error = CreateErrorNode("Underlying parameter class was not found: " + argClassName);
		                    break;
		                }
	                }
	            }
	            
	            if( error == null)
	                try
	                {
	                	SB_ClassMethod method = _dataInterface.LookupMethod(methodNode.GetClass(), methodNode.GetFunction(), classArgs);
	                    methodNode.SetType( method.getSBReturnType(_dataInterface));
	                    methodNode.SetOverlaod(method.overloadIndex);
	                }
	            catch(SB_Exception ex)
	            {
	                error = CreateErrorNode(ex.toString()); 
	            }
	            
	        }   
	    }
	    else //SB_FunctionNode
	    {
	        SB_FunctionNode funcNode = (SB_FunctionNode) (func); 
	        ArrayList params = _dataInterface.LookupFunctionParams(funcNode.GetFunction());
	        error = CheckFunctionArgs(funcNode, params);
	    }
	    
	    
	    if( error != null )
	    {
	        funcWrapper.SetObjectRef(error);
	        result = EParseResult.kBadExpression;
	    }
	    
	    return result;
	}

	public SB_ExpressionNode CheckFunctionArgs(SB_FunctionNode funcNode, ArrayList formalParams )
	{
	    SB_ExpressionNode error = null;
	    // check if there are the correct number of arguments
	    ArrayList params = _dataInterface.LookupFunctionParams(funcNode.GetFunction());
	    
	    int numFormalArgs = params.size();
	    int numActualArgs = funcNode.GetNumArgs(); // count the one being added
	    
	    if (numActualArgs > numFormalArgs) 
	    {
	        // too many params, generate an error
	        error = new SB_ErrorNode(new String( 
	                "Too many arguments for function "
	                + funcNode.GetFunction().toString()), funcNode
	                .GetFunction());
	        
	       return error;
	    } 
	    else 
	    if (numActualArgs < numFormalArgs) 
	    {
	        // too few params, generate an error
	        error = new SB_ErrorNode( new String("Too few arguments for function "
	                        + funcNode.GetFunction()), funcNode.GetFunction());
	        
	        return error;
	    } 
	    else 
	    {
	        // check argument types and directions
	        int argNum = 0;
	        for (int i = 0; i < params.size(); i++, ++argNum) 
	        {
	            FuncParamSpec it = (FuncParamSpec) params.get(i);
	            SB_ParseNode actualArg = funcNode.GetArg(argNum);
	            SB_VarType formalType = (it).type;
	            SB_VarType actualType = actualArg.GetType();
	            
	            if ((formalType != actualType)
	                    && ((formalType != SB_VarType.kAny) && (actualType != SB_VarType.kAny))) 
	            {
	            	//apparent type mismatch - try to recover
	            	if (canExplicitCast(actualType, formalType))
	            	{
	            		//use an explicit casting operator
	            		castParameter(funcNode, argNum, formalType);
	            	}
	            	else if (!_typeManager.canCastTypes(formalType, actualType))
	            	{
	            		// fatal type-mismatch error detected
	            		if (!actualArg.IsValid())
	            		{
	            			// error was already detected in the actual parameter, so don't count it
	            			// as a type mismatch (which would obscure the real error message)
	            			continue;
	            		}
	            	
	                String str = new String("Type mismatch on parameter "
	                        + (argNum + 1) + " of function "
	                        + funcNode.GetFunction() + ":" + " expecting "
	                        + ExpTypeToString(formalType));
	                // ERROR - type mismatch
	                error = new SB_ErrorNode(str, funcNode
	                        .GetFunction());
	                return error;
	            	}
	              
	            } 
	            else 
	            {
	                // check argument directions
	                if (((it).dir == EParamDir.kOut)
	                        || ((it).dir == EParamDir.kInOut)) 
	                {
	                    // since this is an "out" parameter, the actual arg must
	                    // be a variable
	                    if (!(actualArg instanceof SB_VariableNode)) 
	                    {
	                        // ERROR - direction mismatch
	                        error = new SB_ErrorNode(
	                                new String(
	                                        "Parameter "
	                                        + (argNum + 1)
	                                        + " of function "
	                                        + funcNode.GetFunction()
	                                        + " is an 'out' param and requires a variable argument"),
	                                        funcNode.GetFunction());

	                        return error;
	                    }
	                }
	            }
	        }
	    }
	    
	    return error;
	}
	
	private boolean canExplicitCast(SB_VarType fromType, SB_VarType toType)
	{
		if (fromType.equals(SB_VarType.kEntity))
			return (toType.equals(SB_VarType.kFloat) || 
					toType.equals(SB_VarType.kInteger) || 
					toType.equals(SB_VarType.kDouble) || 
					toType.equals(SB_VarType.kShort) || 
					toType.equals(SB_VarType.kByte));
		
		if (fromType.equals(SB_VarType.kInteger))
			return (toType.equals(SB_VarType.kFloat) || 
					toType.equals(SB_VarType.kDouble) || 
					toType.equals(SB_VarType.kShort) || 
					toType.equals(SB_VarType.kByte) || 
					toType.equals(SB_VarType.kEntity));

		if (fromType.equals(SB_VarType.kFloat))
			return (toType.equals(SB_VarType.kInteger) || 
					toType.equals(SB_VarType.kDouble) || 
					toType.equals(SB_VarType.kShort) || 
					toType.equals(SB_VarType.kByte) || 
					toType.equals(SB_VarType.kEntity));
		
		if (fromType.equals(SB_VarType.kDouble))
			return (toType.equals(SB_VarType.kFloat) || 
					toType.equals(SB_VarType.kInteger) || 
					toType.equals(SB_VarType.kShort) || 
					toType.equals(SB_VarType.kByte) || 
					toType.equals(SB_VarType.kEntity));
		
		if (fromType.equals(SB_VarType.kShort))
			return (toType.equals(SB_VarType.kFloat) || 
					toType.equals(SB_VarType.kDouble) || 
					toType.equals(SB_VarType.kInteger) || 
					toType.equals(SB_VarType.kByte) || 
					toType.equals(SB_VarType.kEntity));
		
		if (fromType.equals(SB_VarType.kByte))
			return (toType.equals(SB_VarType.kFloat) || 
					toType.equals(SB_VarType.kDouble) || 
					toType.equals(SB_VarType.kShort) || 
					toType.equals(SB_VarType.kInteger) || 
					toType.equals(SB_VarType.kEntity));
		
		return false;
	}

	private void castParameter(SB_FunctionNode funcNode, int whichParam, SB_VarType toType)
	{
		SB_ParseNode param = funcNode.GetArg(whichParam);
		SB_UnaryOpNode op = null;
		if (toType.equals(SB_VarType.kEntity))
			op = new SB_UnaryOpNode(param, EUnaryOp.kCastLongOp);
		else if (toType.equals(SB_VarType.kInteger))
			op = new SB_UnaryOpNode(param, EUnaryOp.kCastIntOp);
		else if (toType.equals(SB_VarType.kFloat))
			op = new SB_UnaryOpNode(param, EUnaryOp.kCastFloatOp);
		else if (toType.equals(SB_VarType.kDouble))
			op = new SB_UnaryOpNode(param, EUnaryOp.kCastDoubleOp);
		else if (toType.equals(SB_VarType.kShort))
			op = new SB_UnaryOpNode(param, EUnaryOp.kCastShortOp);
		else if (toType.equals(SB_VarType.kByte))
			op = new SB_UnaryOpNode(param, EUnaryOp.kCastByteOp);

		if (op != null)
			funcNode.ReplaceArg(whichParam, op);
	}
	
    public SB_ExpressionNode CreateTernaryOpNode(SB_ExpressionNode arg1,
            SB_ExpressionNode arg2, SB_ExpressionNode arg3, ETernaryOp op) 
    {
        // no type-checking to be done for ternary operators        
        return new SB_TernaryOpNode((SB_ParseNode) (arg1),
                (SB_ParseNode) (arg2), (SB_ParseNode) (arg3), op);
    }

    public SB_ExpressionNode CreateBinaryOpNode(SB_ExpressionNode arg1,
			SB_ExpressionNode arg2, EBinaryOp op) 
    {
    	int asdf;
    	if(arg1 instanceof SB_StringConstantNode)
    		asdf = 0;
    	
      ReturnType value = AreTypesValid(arg1, arg2, op);
			if (value.error != null) 
			{
			    return value.error;
			}
	
			return new SB_BinaryOpNode((SB_ParseNode) (arg1),
					(SB_ParseNode) (arg2), op, value.type);
	}

    /**
     * Lookup the given vartype and see if it matches a known
     * analog to an SB_Type: Integer, Float, Long, String, Boolean
     * 
     * If so, convert it to the SB_Type
     * 
     * @param varType the SB_VarType to convert
     * @return the analogous internal SB_VarType if found,  varType otherwise
     */
    private SB_VarType getEquivalentClassType(SB_VarType varType)
    {
        String className = _dataInterface.LookupClassName(varType);
        
        if( Integer.class.getName().equals(className) )
            return SB_VarType.kInteger;
        if( Float.class.getName().equals(className) )
            return SB_VarType.kFloat;
        if( Double.class.getName().equals(className) )
            return SB_VarType.kFloat;
        if( Long.class.getName().equals(className) )
            return SB_VarType.kEntity;
        if( String.class.getName().equals(className) )
            return SB_VarType.kString;
        if( Boolean.class.getName().equals(className) )
            return SB_VarType.kBoolean;
        
        return varType;
        
    }
    
	private ReturnType AreTypesValid(SB_ExpressionNode arg1,
			SB_ExpressionNode arg2, EBinaryOp op) 
	{
	    //	  one or both args has already been determined to have errors,
			// so just pass them through
	    if( !arg1.IsValid() )
	        return new ReturnType(arg1);
	    
	    if( !arg2.IsValid() )
	        return new ReturnType(arg2);
		
	    if (op == EBinaryOp.kDotOp)
	    		return CheckMemberAccess(arg1,arg2,op);
	    
		SB_VarType returnType = null;
		
		boolean validTypes = false;
		SB_VarType arg1Type = ((SB_ParseNode) arg1).GetType();
		if(arg1Type.getState() >= SB_VarType.kCLASS_START.getState())
		    arg1Type = getEquivalentClassType(arg1Type);
		
		SB_VarType arg2Type = ((SB_ParseNode) arg2).GetType();
		if(arg2Type.getState() >= SB_VarType.kCLASS_START.getState())
		    arg2Type = getEquivalentClassType(arg2Type);

		// verify that the argument types are valid for the specified operation
		if (arg1Type == SB_VarType.kVector) 
		{
			if (arg2Type == SB_VarType.kVector)
				validTypes = (op == EBinaryOp.kAddOp)
						|| (op == EBinaryOp.kSubOp)
						|| (op == EBinaryOp.kMultOp)
						|| (op == EBinaryOp.kDivOp) || (op == EBinaryOp.kEqlOp)
						|| (op == EBinaryOp.kNeqOp);
			else if (arg2Type == SB_VarType.kFloat)
				validTypes = (op == EBinaryOp.kMultOp)
						|| (op == EBinaryOp.kDivOp);
			else if (arg2Type == SB_VarType.kInteger)
				validTypes = (op == EBinaryOp.kMultOp)
						|| (op == EBinaryOp.kDivOp);
			else if (arg2Type == SB_VarType.kString)
            {
                if (op == EBinaryOp.kAddOp)
                    validTypes = true;
                else if (op == EBinaryOp.kDotOp)
                {
                    String rhs = ((SB_StringConstantNode)arg2).GetStringValue();
                    validTypes = rhs.equals("x") || rhs.equals("y") || rhs.equals("z");
                }
                else
                    validTypes = false;
            }
			else if (arg2Type == SB_VarType.kData)
				validTypes = false;
			else if (arg2Type == SB_VarType.kEntity)
				validTypes = false;
			else if (arg2Type == SB_VarType.kBoolean)
				validTypes = false;
			else if (arg2Type == SB_VarType.kAny)
				validTypes = (op == EBinaryOp.kAddOp)
						|| (op == EBinaryOp.kSubOp)
						|| (op == EBinaryOp.kMultOp)
						|| (op == EBinaryOp.kDivOp) || (op == EBinaryOp.kEqlOp)
						|| (op == EBinaryOp.kNeqOp) || (op == EBinaryOp.kDotOp);
		} 
		else 
		if (arg1Type == SB_VarType.kFloat) 
		{
			if (arg2Type == SB_VarType.kVector)
				validTypes = (op == EBinaryOp.kMultOp);
			else if (arg2Type == SB_VarType.kFloat)
				validTypes = true;
			else if (arg2Type == SB_VarType.kInteger)
				validTypes = true;
			else if (arg2Type == SB_VarType.kString)
				validTypes = (op == EBinaryOp.kAddOp);
			else if (arg2Type == SB_VarType.kData)
				validTypes = false;
			else if (arg2Type == SB_VarType.kEntity)
				validTypes = false;
			else if (arg2Type == SB_VarType.kBoolean)
				validTypes = false;
			else if (arg2Type == SB_VarType.kAny)
				validTypes = true;
		} 
		else
		if (arg1Type == SB_VarType.kInteger) 
		{
			if (arg2Type == SB_VarType.kVector)
				validTypes = (op == EBinaryOp.kMultOp);
			else if (arg2Type == SB_VarType.kFloat)
				validTypes = true;
			else if (arg2Type == SB_VarType.kInteger)
				validTypes = true;
			else if (arg2Type == SB_VarType.kString)
				validTypes = (op == EBinaryOp.kAddOp);
			else if (arg2Type == SB_VarType.kData)
				validTypes = false;
			else if (arg2Type == SB_VarType.kEntity)
				validTypes = false;
			else if (arg2Type == SB_VarType.kBoolean)
				validTypes = false;
			else if (arg2Type == SB_VarType.kAny)
				validTypes = true;
		} 
		else 
		if (arg1Type == SB_VarType.kString) 
		{
			if (arg2Type == SB_VarType.kVector)
				validTypes = (op == EBinaryOp.kAddOp);
			else if (arg2Type == SB_VarType.kFloat)
				validTypes = (op == EBinaryOp.kAddOp);
			else if (arg2Type == SB_VarType.kInteger)
				validTypes = (op == EBinaryOp.kAddOp);
			else if (arg2Type == SB_VarType.kString)
				validTypes = (op == EBinaryOp.kAddOp)
						|| (op == EBinaryOp.kEqlOp) || (op == EBinaryOp.kNeqOp)
						|| (op == EBinaryOp.kLeqOp) || (op == EBinaryOp.kLssOp)
						|| (op == EBinaryOp.kGrtOp) || (op == EBinaryOp.kGeqOp);
			else if (arg2Type == SB_VarType.kData)
				validTypes = (op == EBinaryOp.kAddOp);
			else if (arg2Type == SB_VarType.kEntity)
				validTypes = (op == EBinaryOp.kAddOp);
			else if (arg2Type == SB_VarType.kBoolean)
				validTypes = (op == EBinaryOp.kAddOp);
			else if (arg2Type == SB_VarType.kAny)
				validTypes = (op == EBinaryOp.kAddOp)
						|| (op == EBinaryOp.kEqlOp) || (op == EBinaryOp.kNeqOp)
						|| (op == EBinaryOp.kLeqOp) || (op == EBinaryOp.kLssOp)
						|| (op == EBinaryOp.kGrtOp) || (op == EBinaryOp.kGeqOp);
			else if(arg2Type.getState() >= SB_VarType.kCLASS_START.getState())
				validTypes = (op == EBinaryOp.kAddOp);
		} 
		else 
		if (arg1Type == SB_VarType.kData) 
		{
			if (arg2Type == SB_VarType.kVector)
				validTypes = false;
			else if (arg2Type == SB_VarType.kFloat)
				validTypes = false;
			else if (arg2Type == SB_VarType.kInteger)
				validTypes = false;
			else if (arg2Type == SB_VarType.kString)
				validTypes = (op == EBinaryOp.kAddOp);
			else if (arg2Type == SB_VarType.kData)
				validTypes = (op == EBinaryOp.kEqlOp)
						|| (op == EBinaryOp.kNeqOp);
			else if (arg2Type == SB_VarType.kEntity)
				validTypes = false;
			else if (arg2Type == SB_VarType.kBoolean)
				validTypes = false;
			else if (arg2Type == SB_VarType.kAny)
				validTypes = (op == EBinaryOp.kEqlOp)
						|| (op == EBinaryOp.kNeqOp) || (op == EBinaryOp.kAddOp);
		} 
		else if (arg1Type == SB_VarType.kEntity) 
		{
			if (arg2Type == SB_VarType.kVector)
				validTypes = false;
			else if (arg2Type == SB_VarType.kFloat)
				validTypes = false;
			else if (arg2Type == SB_VarType.kInteger)
				validTypes = false;
			else if (arg2Type == SB_VarType.kString)
				validTypes = (op == EBinaryOp.kAddOp);
			else if (arg2Type == SB_VarType.kData)
				validTypes = false;
			else if (arg2Type == SB_VarType.kEntity)
				validTypes = (op == EBinaryOp.kEqlOp)
						|| (op == EBinaryOp.kNeqOp);
			else if (arg2Type == SB_VarType.kBoolean)
				validTypes = false;
			else if (arg2Type == SB_VarType.kAny)
				validTypes = (op == EBinaryOp.kEqlOp)
						|| (op == EBinaryOp.kNeqOp) || (op == EBinaryOp.kAddOp);
		} 
		else 
		if (arg1Type == SB_VarType.kBoolean) 
		{
			if (arg2Type == SB_VarType.kVector)
				validTypes = false;
			else if (arg2Type == SB_VarType.kFloat)
				validTypes = false;
			else if (arg2Type == SB_VarType.kInteger)
				validTypes = false;
			else if (arg2Type == SB_VarType.kString)
				validTypes = (op == EBinaryOp.kAddOp);
			else if (arg2Type == SB_VarType.kData)
				validTypes = false;
			else if (arg2Type == SB_VarType.kEntity)
				validTypes = false;
			else if (arg2Type == SB_VarType.kBoolean)
				validTypes = (op == EBinaryOp.kEqlOp)
						|| (op == EBinaryOp.kNeqOp) || (op == EBinaryOp.kAndOp)
						|| (op == EBinaryOp.kOrOp);
			else if (arg2Type == SB_VarType.kAny)
				validTypes = (op == EBinaryOp.kEqlOp)
						|| (op == EBinaryOp.kNeqOp) || (op == EBinaryOp.kAddOp)
						|| (op == EBinaryOp.kAndOp) || (op == EBinaryOp.kOrOp);
		} 
		else 
		if (arg1Type == SB_VarType.kAny) 
		{
			if (arg2Type == SB_VarType.kVector)
				validTypes = (op == EBinaryOp.kAddOp)
						|| (op == EBinaryOp.kSubOp)
						|| (op == EBinaryOp.kMultOp)
						|| (op == EBinaryOp.kDivOp) || (op == EBinaryOp.kEqlOp)
						|| (op == EBinaryOp.kNeqOp);
			else if (arg2Type == SB_VarType.kFloat)
				validTypes = true;
			else if (arg2Type == SB_VarType.kInteger)
				validTypes = true;
			else if (arg2Type == SB_VarType.kString)
				validTypes = (op == EBinaryOp.kAddOp)
						|| (op == EBinaryOp.kEqlOp) || (op == EBinaryOp.kNeqOp)
						|| (op == EBinaryOp.kLeqOp) || (op == EBinaryOp.kLssOp)
						|| (op == EBinaryOp.kGrtOp) || (op == EBinaryOp.kGeqOp);
			else if (arg2Type == SB_VarType.kData)
				validTypes = (op == EBinaryOp.kEqlOp)
						|| (op == EBinaryOp.kNeqOp) || (op == EBinaryOp.kAddOp);
			else if (arg2Type == SB_VarType.kEntity)
				validTypes = (op == EBinaryOp.kEqlOp)
						|| (op == EBinaryOp.kNeqOp) || (op == EBinaryOp.kAddOp);
			else if (arg2Type == SB_VarType.kBoolean)
				validTypes = (op == EBinaryOp.kEqlOp)
						|| (op == EBinaryOp.kNeqOp) || (op == EBinaryOp.kAddOp)
						|| (op == EBinaryOp.kAndOp) || (op == EBinaryOp.kOrOp);
			else if (arg2Type == SB_VarType.kAny)
				validTypes = true;
		}

		if( !validTypes )
		    return new ReturnType(CreateErrorNode("Invalid argument type for binary operator " + op.toString()));
		
		ReturnType value = new ReturnType();
		value.type = DetermineResultType(arg1, arg2, op);  
		return value;
	}

	public SB_ExpressionNode CreateUnaryOpNode(SB_ExpressionNode arg,
			EUnaryOp op) {
		if (!IsTypeValid(arg, op)) {
			return new SB_ErrorNode("Invalid argument type for unary operator",
					"");
		}

		return new SB_UnaryOpNode((SB_ParseNode) (arg), op);
	}

	private boolean IsTypeValid(SB_ExpressionNode arg, EUnaryOp op) {
		boolean validTypes = false;

		SB_VarType argType = ((SB_ParseNode) arg).GetType();

		// verify that the argument types are valid for the specified operation
		if (op == EUnaryOp.kNotOp)
			validTypes = true;
		else if (op == EUnaryOp.kNegateOp)
			validTypes = (argType == SB_VarType.kFloat)
					|| (argType == SB_VarType.kInteger)
					|| (argType == SB_VarType.kVector);
		else if (op == EUnaryOp.kParenOp)
			validTypes = true;

		// the "any" type is valid in any unary expression
		if (argType == SB_VarType.kAny)
			validTypes = true;
		return validTypes;
	}

	private String ExpTypeToString(SB_VarType type) {
		return _typeManager.getTypeName(type);
	}

	/**
	 * 
	 * @param arg1
	 * @param arg2
	 * @param op
	 * @return SB_VarType if function successful, SB_ExpressionNode otherwise
	 */
	ReturnType CheckMemberAccess(SB_ExpressionNode arg1, SB_ExpressionNode arg2,EBinaryOp op)
	{
	    ReturnType returnValue = new ReturnType();
	    
	    SB_VarType arg1Type = ((SB_ParseNode) arg1).GetType();
			SB_VarType arg2Type = ((SB_ParseNode) arg2).GetType();

		if ((arg1Type != SB_VarType.kStaticClass) && (arg1Type != SB_VarType.kVector) && (arg1Type.getState() < SB_VarType.kCLASS_START.getState()))
		{
		    returnValue.error = CreateErrorNode("Left-hand argument to . operator must be a class variable, vector variable, or class name.");
		    return returnValue;
		}
		
		if (arg1Type == SB_VarType.kVector)
		{
		    // vector component access
		    if( arg2 instanceof SB_StringConstantNode )
		    {
		        SB_StringConstantNode member = (SB_StringConstantNode) arg2;
			
					if ((member.GetStringValue().compareTo("x") ==0) || 
					    (member.GetStringValue().compareTo("y") ==0) || 
					    (member.GetStringValue().compareTo("z") ==0))
					{
					    returnValue.type = SB_VarType.kFloat;
							return returnValue;
					}
				}
		    
		    
		    returnValue.error = CreateErrorNode("Right-hand side of the . operator must be x, y, or z for a vector.");
		    return returnValue;
		}

		// method invocation or member variable access
		if (arg2 instanceof SB_ErrorNode)
			// must be bad method -- pass the error through
			return returnValue;

		if (arg2 instanceof SB_MethodNode)
		{
		    returnValue.type = arg2Type;
		    return returnValue; 
		}

		// member variable access - make sure that the member belongs to the class
		String className,memberName;
		
		if (arg1 instanceof SB_ClassNode)
		{
			String alias  = ((SB_ClassNode)arg1).GetClass();
			className = _dataInterface.LookupClassName(alias);
		} 
		else if (arg1 instanceof SB_VariableNode)
		{
			SB_VarType variableType = ((SB_VariableNode)arg1).GetType();
			className = _dataInterface.LookupClassName(variableType);
		}
		else
		{
		    returnValue.error = CreateErrorNode("Can't find class for: " + arg1.toString());
		    return returnValue;
		}
		
		if (arg2 instanceof SB_StringConstantNode)
		{
			memberName = ((SB_StringConstantNode)arg2).GetStringValue();
		}
		else
		{
		    returnValue.error = CreateErrorNode("Class member must be a string");
		    return returnValue;
		}

		SB_VarType retType = _dataInterface.LookupMember(className, memberName);
		
		if (retType == SB_VarType.kNoSuchName)
		    returnValue.error = CreateErrorNode("Unknown member variable : " + memberName);
		
		returnValue.type = retType;
		return returnValue;
	}


	SB_VarType DetermineResultType(SB_ExpressionNode arg1,SB_ExpressionNode arg2,EBinaryOp op)
	{
		// assume that the argument types are valid (this should have been
		// checked during parsing) and that the operator is not dot
	  SB_VarType arg1Type = ((SB_ParseNode) arg1).GetType();
	  SB_VarType arg2Type = ((SB_ParseNode) arg2).GetType();
	  

      if(arg1Type.getState() >= SB_VarType.kCLASS_START.getState())
          arg1Type = getEquivalentClassType(arg1Type);
      
      if(arg2Type.getState() >= SB_VarType.kCLASS_START.getState())
          arg2Type = getEquivalentClassType(arg2Type);
      
		if ((op == EBinaryOp.kLssOp) || (op == EBinaryOp.kLeqOp) || (op == EBinaryOp.kEqlOp) || 
			(op == EBinaryOp.kGrtOp) || (op == EBinaryOp.kGeqOp) || (op == EBinaryOp.kNeqOp) ||
			(op == EBinaryOp.kAndOp) || (op == EBinaryOp.kOrOp))
		{                
			// relational and logical operators always return a boolean
			return SB_VarType.kBoolean;
		}
		else if ((arg1Type == SB_VarType.kString) || (arg2Type == SB_VarType.kString))
		{
			// string concatenation turns anything paired 
			// with a string into a string as well
			return SB_VarType.kString;
		}
		else if ((arg1Type == SB_VarType.kVector) || (arg2Type == SB_VarType.kVector))
		{
			// any arithmetic operation on a vector results in another vector
			return SB_VarType.kVector;
		}
		else
		if( (arg1Type == SB_VarType.kFloat || arg1Type == SB_VarType.kInteger || arg1Type == SB_VarType.kAny) &&
		    (arg2Type == SB_VarType.kFloat || arg2Type == SB_VarType.kInteger || arg2Type == SB_VarType.kAny) )  
		{
			// no operators return a value of type Data or Entity
			if ((arg1Type == SB_VarType.kFloat) || (arg2Type == SB_VarType.kFloat))
			{
				// if either argument to an arithmetic operator is a float,
				// the result becomes a float
				return SB_VarType.kFloat;
			}
			else
			{
				return SB_VarType.kInteger;
			}
		}

		return SB_VarType.kNoSuchName;	
	}
	
	class ReturnType
	{
	    public SB_VarType type;
	    public SB_ExpressionNode error;
	    
	    public ReturnType()
	    {
	        type = null;
	        error = null;
	    }
	    
	    public ReturnType(SB_ExpressionNode e)
	    {
	        error = e;
	    }
	}
}